﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Apollo;
using Apollo.Core;
using NHibernate.Mapping;
using Apollo.Web;

namespace Apollo.Website
{
    public partial class Controls_EntityGridView : System.Web.UI.UserControl
    {
        public int currentEntityViewId { get; set; }

        private EntityView entityView;
        public EntityView EntityView
        {
            get
            {
                if (entityView == null)
                    entityView = Apollo.Application.Meta<EntityView>()
                                    .SingleOrDefault<EntityView>(ev => ev.Id == currentEntityViewId);

                return entityView;
            }
            set
            {
                this.currentEntityViewId = value == null ? 0 : value.Id;
                this.entityView = value;
            }
        }

        public UserView UserView { get; set; }

        public EntityGridBehavior Behavior { get; set; }

        public string FilterString { get; set; }

        public string Prefix { get { return this.ClientID; } }

        public string Columns { get; set; }

        public string EditUrl { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            PopulateGrid();
        }

        protected void PopulateGrid()
        {
            if (this.EntityView == null && this.UserView == null)
                return;

            // get the correct columns
            if (string.IsNullOrEmpty(this.Columns))
            {
                if (this.EntityView != null)
                    this.Columns = this.EntityView.ViewString;
                else
                    this.Columns = this.UserView.ViewString;
            }

            // get the base entity
            Entity entity = this.EntityView != null ? this.EntityView.Entity : this.UserView.Entity;
            
            // create the projection manager
            ProjectionManager manager = new ProjectionManager(this.Columns, entity.MappedClass);

            // check if the manager has aggregate methods
            if (this.Behavior == EntityGridBehavior.Normal && manager.HasAggregatedColumns)
                this.Behavior = EntityGridBehavior.Aggregated;


            string colModel = "";

            IList<string> widths = new List<string>();
            if (string.IsNullOrEmpty(this.Columns) && this.EntityView != null && !string.IsNullOrEmpty(this.EntityView.ViewWidths))
                widths = new List<string>(this.EntityView.ViewWidths.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));

            for (int index = 0; index < manager.Items.Count; index++)
            {
                Projection projection = manager.Items[index].Projection;

                string width = ", width: 100";
                if (widths.Count > index)
                    width = ", width: " + widths[index];

                if (colModel != "")
                    colModel += ",";

                string columnFullName = manager.Items[index].Aggregation == ProjectionAggregate.None
                    ? projection.ProjectionPath
                    : manager.Items[index].Aggregation.ToString() + "(" + projection.ProjectionPath + ")";
                string columnDisplayFullName = manager.Items[index].Aggregation == ProjectionAggregate.None
                    ? projection.DisplayName.Replace("'", "\\'")
                    : manager.Items[index].Aggregation.ToString() + "(" + projection.DisplayName.Replace("'", "\\'") + ")";

                colModel += "{ name: '" + projection.ProjectionPath
                    + "', index: '" + columnFullName + "'"
                    + width
                    + ", fieldType: '" + projection.PropertyTypeForJSON + "'"
                    + ", displayName: '" + columnDisplayFullName + "'"
                    + this.GetAlign(projection.PropertyType)
                    + this.GetFormater(projection, manager.Items[index].Aggregation)
                    + ", aggregate: '" + manager.Items[index].Aggregation.ToString() + "'"
                    + " }";
            }

            // get the dlbClick url
            string editUrl = "";
            string queryString = this.EntityView != null ? "ViewId=" + this.EntityView.Id.ToString() : "UserViewId=" + this.UserView.Id.ToString();
            if (this.Behavior == EntityGridBehavior.Normal || this.Behavior == EntityGridBehavior.None || this.Behavior == EntityGridBehavior.QuickSearch)
            {
                editUrl = this.EntityView != null && !string.IsNullOrEmpty(this.EntityView.CustomEditUrl) ? this.EntityView.CustomEditUrl : (
                                    !string.IsNullOrEmpty(entity.CustomEditUrl) ? entity.CustomEditUrl :
                                        "Edit.aspx?EntityId=" + entity.Id.ToString() + "&ObjectId={ObjectId}"
                    );
                
                if (!string.IsNullOrEmpty(this.EditUrl))
                    editUrl = this.EditUrl;
            }
            else if (this.Behavior == EntityGridBehavior.Aggregated) {

                string nonAggregatedFilter = "";
                string nonAggregatedColumns = "";
                int idx = 0;
                foreach (ProjectionManagerItem item in manager.Items) {
                    if (nonAggregatedColumns != "")
                        nonAggregatedColumns += ",";

                    if (item.Aggregation < 0) {
                        nonAggregatedColumns += item.Aggregation + "("
                            + item.Projection.ProjectionPath
                            + ")";
                    }
                    else
                        nonAggregatedColumns += item.Projection.ProjectionPath;

                    if (item.Aggregation == 0) {
                        if (nonAggregatedFilter != "")
                            nonAggregatedFilter += ",";

                        if (item.IdentifierProjection != null)
                            nonAggregatedFilter += "{\"field\":\"" + item.IdentifierProjection.ProjectionPath + "\",\"op\":\"eq\",\"data\":\"{" + idx + "}\"}";
                        else
                            nonAggregatedFilter += "{\"field\":\"" + item.DisplayProjection.ProjectionPath + "\",\"op\":\"eq\",\"data\":\"{" + idx + "}\"}";

                        idx++;
                    }
                }

                nonAggregatedFilter = "{\"groupOp\":\"AND\",\"groups\":[],\"rules\":["+nonAggregatedFilter+"]}";

                // format it to be used with javascript
                nonAggregatedFilter = nonAggregatedFilter.Replace("\\", "\\\"").Replace("\"", "\\\"");

                editUrl = "/QuickView.aspx?" + queryString + "&b=n&c=" + nonAggregatedColumns + "&f=" + nonAggregatedFilter;
            }


            queryString += "&f=" + this.FilterString;
            if (!string.IsNullOrEmpty(this.Columns))
                queryString += "&c=" + this.Columns;

            // rights definitions
            bool enableCreate = Apollo.Application.Current.IsAllowed(Apollo.Application.ACTION_CREATE_NAME, entity, null);
            bool enableFilter = (this.Behavior == EntityGridBehavior.Normal || this.Behavior == EntityGridBehavior.Aggregated)
                && Apollo.Application.Current.IsAllowed(Apollo.Application.ACTION_FILTER, entity, null);
            bool enableUserView = this.Behavior == EntityGridBehavior.Normal;
            bool enableExtraColumns = (this.Behavior == EntityGridBehavior.Normal || this.Behavior == EntityGridBehavior.Aggregated)
                && Apollo.Application.Current.IsAllowed(Apollo.Application.ACTION_EXTRACOLUMNS, entity, null);
            bool enableExport = (this.Behavior != EntityGridBehavior.QuickSearch);
            bool enableClose = this.Behavior == EntityGridBehavior.QuickSearch;

            if (this.UserView != null)
            {
                this.FilterString = this.UserView.Filter;

                if(!string.IsNullOrEmpty(this.FilterString))
                    this.FilterString = this.FilterString.Replace("\\", "\\\\").Replace("\"", "\\\"");
            }

            LiteralJS.Text += @"
    <script type=""text/javascript"">
        function CreateGrid" + this.Prefix + @"() {
try{
            new apolloJQGrid(
                '#entity_list" + this.Prefix + @"', 
                '#entity_pager" + this.Prefix + @"', 
                '#entity_menu" + this.Prefix + @"', 
                {
                    behavior: """ + this.Behavior.ToString().ToLower() + @""",
                    relationsIdentifier: ""entity_sub_items" + this.Prefix + @""",
                    ajaxUrl: ""JSONEntityList.ashx?" + queryString + @""",
                    dblClickUrl: """ + editUrl + @""",
                    columnModels: [" + colModel + @"],
                    entityID: " + entity.Id + @",
                    enableCreate: " + enableCreate.ToString().ToLower() +@",
                    enableQuickFiler: true,
                    enableFilter: " + enableFilter.ToString().ToLower() + @",
                    filterString: """ + this.FilterString + @""",
                    enableExport: " + enableExport.ToString().ToLower() + @",
                    enableUserView: " + enableUserView.ToString().ToLower() + @",
                    enableExtraColumns: " + enableExtraColumns.ToString().ToLower() + @",
                    enableClose: " + enableClose.ToString().ToLower() + @"
                });
}catch(ex) {alert(ex);} 
        }

    </script>
";

            PopulateSubItems(entity);

            if(enableExport)
                CreateExportToCsvButton(entity, this.EntityView != null ? this.EntityView.Filter : this.UserView.Filter);
        }

        
        protected void PopulateSubItems(Entity entity)
        {
            LiteralSubItems.Text = "";

            foreach (EntityProperty property in Apollo.Application.Meta<EntityProperty>()
                        .Where<EntityProperty>(ep => ep.BelongToEntity != null && ep.BelongToEntity.Id == entity.Id
                            && ep.ReverseRelationListView != null))
            {
                string filter =
                    "{ \"groupOp\": \"AND\",\"groups\" : [],\"rules\" : [ {"
                    + "\"field\": \"" + property.PropertyName + "." + entity.IdentifierProperty + "\", "
                    + "\"op\": \"eq\", "
                    + "\"data\": \"" + Request["ObjectId"] + "\""
                    + "} ] }";

                LanguageDefinition ld = Apollo.Application.Meta<LanguageDefinition>()
                    .SingleOrDefault<LanguageDefinition>(l => l.Type == "ReverseProperty"
                        && l.CodeHi == property.Entity.Name
                        && l.CodeLo == property.PropertyName
                        && l.Language.Id == Apollo.Application.CurrentLanguage.Id);

                LiteralSubItems.Text += "<option value='" + property.ReverseRelationListView.Id.ToString() + "|" + property.PropertyName + "." + entity.IdentifierProperty + "'>"
                    + (ld == null ? property.Entity.Name + " - " + property.PropertyName : ld.DisplayName)
                    + "</option>";
            }
        }

        protected void CreateExportToCsvButton(Entity entity, string filterString)
        {
            LiteralExportToCSV.Text = "<span><a href=\"#\" onclick='CSVOpen("
                + entity.Id + ", \""
                + this.FilterString + "\", \""
                + this.Columns + "\", \""
                + entity.IdentifierProperty + "\", \"#entity_list"
                + this.Prefix
                + "\");'><img src=\"/css/icons/icon_88.png\" border=\"0\" /> Export to csv</a></span>";
        }

        protected string GetAlign(Type t)
        {
            string align = "";
            if (t == typeof(int))
                align = ", align: 'right'";
            else if (t == typeof(decimal))
                align = ", align: 'right'";
            else if (t == typeof(float))
                align = ", align: 'right'";
            else if (t == typeof(double))
                align = ", align: 'right'";
            else if (t == typeof(DateTime))
                align = ", align: 'center'";
            else if (t == typeof(Boolean))
                align = ", align: 'center'";

            return align;
        }

        protected string GetFormater(Projection projection, ProjectionAggregate aggregation)
        {
            if (aggregation < 0) {
                switch (aggregation) { 
                    case ProjectionAggregate.Date:
                        return ", formatter: apolloDate";
                    case ProjectionAggregate.Month:
                        return ", formatter: apolloMonth";
                    case ProjectionAggregate.Quarter:
                        return ", formatter: apolloQuarter";
                    default:
                        return "";
                }
            }

            if (projection.IsRelation)
                return "";

            EntityProperty property = Apollo.Application.Meta<EntityProperty>()
                        .SingleOrDefault<EntityProperty>(ep => ep.Entity.Name == projection.ClassType.Name
                        && ep.PropertyName == projection.PropertyName);

            if (property == null)
                return "";

            if (property.Type != EntityPropertyType.Enum || property.Enum == null)
                return "";

            // create js formater for enums
            string formaterFunction = "";

            foreach (EnumValue ev in Apollo.Application.Meta<EnumValue>()
                .Where<EnumValue>(e => e.Enum.Id == property.Enum.Id))
                formaterFunction += "case \"" + ev.Value + "\": return \"" + ev.Name + "\";";

            string uniqueId = DateTime.Now.Millisecond.ToString() + DateTime.Now.Second.ToString();

            LiteralJS.Text += @"
    <script type=""text/javascript"">
            function apolloFormater" + uniqueId + @"(cellvalue, options, rowObject){
                switch(cellvalue){
                    " + formaterFunction + @"
                    default: return """";
                }
            }
    </script>
";
            return ", formatter: apolloFormater" + uniqueId;
        }

        public string GetJavaScriptMethodDefinition()
        {
            if (this.UserView == null && this.EntityView == null)
                return "";

            return "CreateGrid" + this.Prefix + "();";
        }

        public enum EntityGridBehavior { 
            Normal, None, QuickSearch, Aggregated
        }
    }
}